const isProxy = Symbol("isProxy");
const MountPropertyName = "__WatchGroups_";
function checkMount(target: any, mountName: string, defaultValue: any) {
    if(target.constructor[mountName] == undefined) {
        Object.defineProperty(target.constructor, mountName, {
            value: defaultValue
        });
    }
}
export function Watch(group: string) {
    let isInit = true;
    function setValueProxy(val: any, callback: () => void) {
        let type = Object.prototype.toString.call(val);
        if(type == '[object Object]') {
            for(let key in val) {
                val[key] = setValueProxy(val[key], callback);
            }
            return new Proxy(val, {
                get: (target, idx: string) => {
                    if(<any>idx == isProxy) {
                        return true;
                    }
                    return target[idx];
                },
                // @ts-ignore
                set: (target, idx: number, value: any) => {
                    if(value[isProxy] == true) {
                        target[idx] = value;
                    } else {
                        target[idx] = setValueProxy(value, callback);
                    }
                    callback();
                    return true;
                }
            });
        }
        if(type == '[object Array]') {
            for(let i = 0; i < val; i++) {
                try {
                    if(val[i][isProxy] == false) {
                        val[i] = setValueProxy(val[i], callback);
                    }                    
                } catch (error) {
                    
                }
            }
            return new Proxy(val, {
                get: (target, idx: string) => {
                    if(<any>idx == isProxy) {
                        return true;
                    }
                    return target[idx];
                },
                // @ts-ignore
                set: (target, idx: number, value: any) => {
                    if(value[isProxy] == true) {
                        target[idx] = value;
                    } else {
                        target[idx] = setValueProxy(value, callback);
                    }
                    if(<any>idx != 'length') {
                        callback();
                    }
                    return true;
                }
            });
            
        }
        return val;
    }
    return function(target: any, propertyKey: string) {
        checkMount(target, MountPropertyName, {});
        if(target.constructor[MountPropertyName].hasOwnProperty(group) == false) {
            target.constructor[MountPropertyName][group] = [];
        }
        let _value: any = undefined;
        const descriptor: PropertyDescriptor = {
            get: () => {
                return _value;
            },
            set: function(value) {
                _value = setValueProxy(value, () => {
                    for(let funcName of target.constructor[MountPropertyName][group]) {
                        (<any>this)[funcName].apply(this);
                    }
                });
                if(isInit == true) {
                    isInit = false;
                    return;
                }
                for(let funcName of target.constructor[MountPropertyName][group]) {
                    (<any>this)[funcName].apply(this);
                }
            }
        };
        return <any>descriptor;
    }
}
export function Reference(group: string) {
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        checkMount(target, MountPropertyName, {});
        if(target.constructor[MountPropertyName].hasOwnProperty(group) == false) {
            target.constructor[MountPropertyName][group] = [];
        }
        target.constructor.__WatchGroups_[group].push(propertyKey);
    }
}
export class Example {
    @Watch('font')
    public font: any = null;
    @Reference('font')
    fontChange1() {
        console.log('fontChange1');
    }
    @Reference('font')
    fontChange2() {
        console.log('fontChange2');
    }
}
let exp = new Example();
// console.log(exp)
// exp.font = [];
// // console.log(exp.font);
// // console.log('push');
// exp.font.push(1)
export function start() {
    // to do
}